/* @(#)DecKanGlGlyph.java
** 
** Copyright (C) 2012 by Steven D. Stamps
**
**             Trademarks & Copyrights
** Flywheel Management Science(TM), Flywheel Management Model(TM),
** Flywheel Story Editor(TM) and FlywheelMS(TM) are exclusive trademarks
** of Steven D. Stamps and may only be used freely for the purpose of
** identifying the unforked version of this software.  Subsequent forks
** may not use these trademarks.  All other rights are reserved.
** and FlywheelMS(TM) are exclusive trademarks of Steven D. Stamps
** and may only be used freely for the purpose of identifying the
** unforked version of this software.  Subsequent forks (if any) may
** not use these trademarks.  All other rights are reserved.
**
** DecKanGL (Decorated Kanban Glyph Language) and TribKn (Tribal Knowledge)
** are also exclusive trademarks of Steven D. Stamps.  These may be used
** freely within the unforked FlywheelMS application and documentation.
** All other rights are reserved.
**
** gConGUI (game Controller Graphical User Interface) is an exclusive
** trademark of Steven D. Stamps.  This trademark may be used freely
** within the unforked FlywheelMS application and documentation.
** All other rights are reserved.
**
** Trademark information is available at
** <http://www.flywheelms.com/trademarks>
**
** Flywheel Management Science(TM) is a copyrighted body of management
** metaphors, governance processes, and leadership techniques that is
** owned by Steven D. Stamps.  These copyrighted materials may be freely
** used, without alteration, by the community (users and developers)
** surrounding this GPL3-licensed software.  Additional copyright
** information is available at <http://www.flywheelms.org/copyrights>
**
**              GPL3 Software License
** This program is free software: you can use it, redistribute it and/or
** modify it under the terms of the GNU General Public License, version 3,
** as published by the Free Software Foundation. This program is distributed
** in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
** even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
** PURPOSE.  See the GNU General Public License for more details. You should
** have received a copy of the GNU General Public License, in a file named
** COPYING, along with this program.  If you cannot find your copy, see
** <http://www.gnu.org/licenses/gpl-3.0.html>.
*/

package com.flywheelms.library.deckangl.glyph;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;

import com.flywheelms.library.FmsLibraryApplication;
import com.flywheelms.library.R;
import com.flywheelms.library.deckangl.enumerator.DecoratorCanvasLocation;
import com.flywheelms.library.deckangl.enumerator.DecoratorColor;
import com.flywheelms.library.deckangl.enumerator.GlyphZoomSize;
import com.flywheelms.library.deckangl.interfaces.DecKanGlDecorator;

public class DecKanGlGlyph {
	
//	private static final int width_ANNOTATION_CHARACTER = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__character_width);
//	private static final int aGlyphZoomSize.getAnnotationCharacterHeight() = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__character_height);
//	private static final int height_ANNOTATION_BOTTOM_MARGIN = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__bottom_margin);
//	private static final int height_ANNOTATION_BOTTOM_ZONE = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__bottom_zone);
//	private static final int aGlyphZoomSize.getAnnotationTopMarginHeight() = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__top_margin);
//	private static final int aGlyphZoomSize.getAnnotationTopZoneHeight() = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__top_zone);
//	private static final int aGlyphZoomSize.getAnnotationLeftMarginWidth() = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__left_margin);
//	private static final int width_ANNOTATION_RIGHT_MARGIN = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__right_margin);
//	private static final int aGlyphZoomSize.getDecoratedNounMargin() = FmsLibraryApplication.getContext().getResources().getInteger(R.integer.deckangl_glyph__annotation__zoomed_glyph_margin);
	
	private static final int color_ANNOTATION_BACKGROUND = FmsLibraryApplication.getContext().getResources().getColor(R.color.canvas__noun_background);

	private NounWorkStatus nounWorkStatus;
	private HashMap<DecoratorCanvasLocation, DecKanGlDecorator> decoratorMap;
	private transient Bitmap decoratedNounBitmap;
	private transient Hashtable<GlyphZoomSize, Bitmap> annotatedBitmapHashtable = new Hashtable<GlyphZoomSize, Bitmap>();
	private Date tribKnQualityTimeStamp;
	private GlyphCanvas glyphCanvas;

	public DecKanGlGlyph(
			NounWorkStatus aNounWorkStatus,
			GlyphCanvas aGlyphCanvas) {
		this.nounWorkStatus = aNounWorkStatus;
		this.decoratorMap = aGlyphCanvas.getDecoratorMap();
		this.glyphCanvas = aGlyphCanvas;
		this.tribKnQualityTimeStamp = new Date();
	}
	
	// de-serialize
	public DecKanGlGlyph(
			DecKanGlDictionary aDecKanGlDictionary,
			NounWorkStatus aNounWorkStatus,
			HashMap<DecoratorCanvasLocation, DecKanGlDecorator> aDecKanGlDecoratorMap,
			Date aTimestamp ) {
		this.nounWorkStatus = aNounWorkStatus;
		this.decoratorMap = aDecKanGlDecoratorMap;
		this.glyphCanvas = new GlyphCanvas(aNounWorkStatus.getBitmapResourceId(), aDecKanGlDecoratorMap, aDecKanGlDictionary);
		this.tribKnQualityTimeStamp = aTimestamp;
	}
	
	public Date getTribKnQualityTimeStamp() {
		return this.tribKnQualityTimeStamp;
	}

	public NounWorkStatus getNounWorkStatus() {
		return this.nounWorkStatus;
	}
	
	public GlyphCanvas getGlyphCanvas() {
		return this.glyphCanvas;
	}

	public Bitmap getAnnotatedBitmap(GlyphZoomSize aGlyphZoomSize) {
		if (! this.annotatedBitmapHashtable.containsKey(aGlyphZoomSize)) {
			this.annotatedBitmapHashtable.put(
					aGlyphZoomSize, buildAnnotatedBitmap(aGlyphZoomSize) );
		}
		return this.annotatedBitmapHashtable.get(aGlyphZoomSize);
	}

	public Bitmap getDecoratedNounBitmap() {
		if (this.decoratedNounBitmap == null) {
			this.decoratedNounBitmap = buildDecoratedNounBitmap();
		}
		return this.decoratedNounBitmap;
	}
	
	public Bitmap getDecoratedNounBitmapZoom(GlyphZoomSize aGlyphZoomSize) {
		return Bitmap.createScaledBitmap(getDecoratedNounBitmap(), aGlyphZoomSize.getDecoratedNounWidth(), aGlyphZoomSize.getDecoratedNounHeight(), true);
	}

	private Bitmap buildDecoratedNounBitmap() {
		/*
		 * TODO - bitmap drawing needs to be optimized !!!   SDS
		 * Reference the following URL as a starting point:
		 *     http://www.curious-creature.org/2010/12/08/bitmap-quality-banding-and-dithering/
		 *     
		 *         AND / OR
		 *         
		 * create a Hashtable that contains a custom rendering keyed by GlyphZoomSize
		 */
		Bitmap.Config theBitmapConfig = Bitmap.Config.ARGB_8888;
		Bitmap theBitmap = Bitmap.createBitmap(
				getGlyphCanvas().getWidth(),
				getGlyphCanvas().getHeight(),
				theBitmapConfig);
		theBitmap.setDensity(Bitmap.DENSITY_NONE);
		Canvas theCanvas = new Canvas(theBitmap);
		Paint thePaint = new Paint();
		thePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
		theCanvas.drawBitmap(theBitmap, 0, 0, thePaint);
		thePaint.setXfermode(null);  // reset
		drawDecoratorBackground(theCanvas, thePaint);  // draw background for decorators
		// draw noun Bitmap
		theCanvas.drawBitmap(
				getNounWorkStatus().getBitmap(),
				getGlyphCanvas().getNounStatusImageCanvasPositionX(),
				getGlyphCanvas().getNounStatusImageCanvasPositionY(),
				null );
		// draw decorators
		for (DecKanGlDecorator theDecKanGlDecorator : getDecoratorMap().values()) {
			if (!(theDecKanGlDecorator.getColor() == DecoratorColor.TRANSPARENT) &&
					theDecKanGlDecorator.isEnabled() ) {
				drawDecorator(theDecKanGlDecorator, theCanvas);
			}
		}
		return theBitmap;
	}

	// TODO Needs more work to smooth corners.
	private void drawDecoratorBackground(Canvas aCanvas, Paint thePaint) {
		thePaint.setColor(getGlyphCanvas().getDecoratorBackgroundColor());
        thePaint.setAntiAlias(true);
	 	aCanvas.drawRoundRect(
	 			new RectF(1.0f, 1.0f, (float) aCanvas.getWidth() - 2, (float) aCanvas.getHeight() - 2),
	 			aCanvas.getWidth() / 4,
	 			aCanvas.getHeight() / 6,
	 			thePaint );
	}

	private void drawDecorator(
			DecKanGlDecorator aDecKanGlDecorator,
			Canvas aCanvas ) {
		aCanvas.drawBitmap(
				aDecKanGlDecorator.getBitmap(),
				aDecKanGlDecorator.getTribKnQualityMetric().getLogicalCanvasLocation().getCanvasPositionX(this),
				aDecKanGlDecorator.getTribKnQualityMetric().getLogicalCanvasLocation().getCanvasPositionY(this),
				null );
	}

	private Bitmap buildAnnotatedBitmap(GlyphZoomSize aGlyphZoomSize) {
		Bitmap theDecoratedNounBitmapZoom = getDecoratedNounBitmapZoom(aGlyphZoomSize);
		ArrayList<String> theBottomZoneAnnotationList = getCanvasZoneAnnotations(CanvasZone.BOTTOM_ZONE);
		ArrayList<String> theLeftZoneAnnotationList = getCanvasZoneAnnotations(CanvasZone.LEFT_ZONE);
		ArrayList<String> theRightZoneAnnotationList = getCanvasZoneAnnotations(CanvasZone.RIGHT_ZONE);
		ArrayList<String> theTopZoneAnnotationList = getCanvasZoneAnnotations(CanvasZone.TOP_ZONE);
//		int theBottomZoneAnnotationWidth = (int)(getMaximumAnnotationLength(theBottomZoneAnnotationList) * width_ANNOTATION_CHARACTER);
		int theLeftZoneAnnotationWidth = (getMaximumAnnotationLength(theLeftZoneAnnotationList) * aGlyphZoomSize.getAnnotationCharacterWidth());
		int theRightZoneAnnotationWidth = (getMaximumAnnotationLength(theRightZoneAnnotationList) * aGlyphZoomSize.getAnnotationCharacterHeight());
//		int theTopZoneAnnotationWidth = (int)(getMaximumAnnotationLength(theTopZoneAnnotationList) * width_ANNOTATION_CHARACTER);
		// create new bitmap
		Bitmap.Config theBitmapConfig = Bitmap.Config.ARGB_8888;
		Bitmap theAnnotatedBitmap = Bitmap.createBitmap(
//				aGlyphZoomSize.getAnnotationLeftMarginWidth() + theLeftZoneAnnotationWidth + aGlyphZoomSize.getDecoratedNounMargin() + theDecoratedNounBitmapZoom.getWidth() + aGlyphZoomSize.getDecoratedNounMargin() + theRightZoneAnnotationWidth + aGlyphZoomSize.getAnnotationRightMarginWidth(),
//				aGlyphZoomSize.getAnnotationTopMarginHeight() + aGlyphZoomSize.getAnnotationTopZoneHeight() + (aGlyphZoomSize.getDecoratedNounMargin() * 2) + theDecoratedNounBitmapZoom.getHeight() + aGlyphZoomSize.getAnnotationBottomZoneHeight() + aGlyphZoomSize.getAnnotationBottomMarginHeight() + aGlyphZoomSize.getHeightFudgeFactor(),
				aGlyphZoomSize.getCanvasWidth(),
				aGlyphZoomSize.getCanvasHeight(),
				theBitmapConfig);
		theAnnotatedBitmap.setDensity(Bitmap.DENSITY_NONE);
		Canvas theCanvas = new Canvas(theAnnotatedBitmap);
		Paint thePaint = new Paint();
		thePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
		thePaint.setXfermode(null);  // reset
		thePaint.setColor(color_ANNOTATION_BACKGROUND);
	 	theCanvas.drawRect(
	 			new RectF(1.0f, 1.0f, (float) theCanvas.getWidth() - 2, (float) theCanvas.getHeight() - 2),
	 			thePaint );
		
		theCanvas.drawBitmap(  // draw zoomed glyph
				theDecoratedNounBitmapZoom,
				aGlyphZoomSize.getAnnotationLeftMarginWidth() + theLeftZoneAnnotationWidth + aGlyphZoomSize.getDecoratedNounMargin(),
				5 + (aGlyphZoomSize.getAnnotationTopZoneHeight() * 2) + aGlyphZoomSize.getDecoratedNounMargin(),
				null );
		// setup for writing
		thePaint.setAntiAlias(true);
		thePaint.setStyle(Paint.Style.FILL);
		thePaint.setStrokeWidth(1);
		// write node quality index
		thePaint.setTextSize(aGlyphZoomSize.getNodeQualityTextSize());
		if(getNodeQualityIndex() < 0) {
			thePaint.setColor(Color.RED);
		} else {
			thePaint.setColor(Color.BLACK);
		}
		theCanvas.drawText(
				getNodeQualityIndexString(),
				25,
				25,
				thePaint );
		// setup for writing annotations
		thePaint.setTextSize(aGlyphZoomSize.getAnnotationTextSize());
		thePaint.setColor(Color.BLACK);
		// Top Zone
		int theRow = 0;
		int theLeftMargin = aGlyphZoomSize.getAnnotationLeftMarginWidth() + theLeftZoneAnnotationWidth + aGlyphZoomSize.getDecoratedNounMargin() - 10;
		for (String theAnnotation : theTopZoneAnnotationList) {
			theCanvas.drawText(
					theAnnotation,
					theLeftMargin,
					aGlyphZoomSize.getAnnotationTopMarginHeight() + theRow * aGlyphZoomSize.getAnnotationCharacterHeight(),
					thePaint );
			++theRow;
		}
		// Bottom Zone
		theRow = 0;
		theLeftMargin = aGlyphZoomSize.getAnnotationLeftMarginWidth() + theLeftZoneAnnotationWidth + aGlyphZoomSize.getDecoratedNounMargin() - 10;
		for (String theAnnotation : theBottomZoneAnnotationList) {
			theCanvas.drawText(
					theAnnotation.equals("") ? "- - - - - - - -" : theAnnotation,
					theLeftMargin,
					aGlyphZoomSize.getAnnotationTopMarginHeight() + aGlyphZoomSize.getAnnotationTopZoneHeight() + theDecoratedNounBitmapZoom.getHeight() + (aGlyphZoomSize.getDecoratedNounMargin() * 2) + (theRow * aGlyphZoomSize.getAnnotationCharacterHeight()) + aGlyphZoomSize.getBottomZoneStartFudgeFactor(),
					thePaint );
			++theRow;
		}
		// Left Zone
		theRow = 0;
		theLeftMargin = aGlyphZoomSize.getAnnotationLeftMarginWidth();
		for (String theAnnotation : theLeftZoneAnnotationList) {
			theCanvas.drawText(
					theAnnotation,
					theLeftMargin,
					aGlyphZoomSize.getAnnotationTopMarginHeight() + aGlyphZoomSize.getAnnotationTopZoneHeight() + (aGlyphZoomSize.getDecoratedNounMargin() * 2) + (theRow * aGlyphZoomSize.getAnnotationCharacterHeight()) + 35,
					thePaint );
			++theRow;
		}
		// Right Zone
		theRow = 0;
		theLeftMargin = aGlyphZoomSize.getAnnotationLeftMarginWidth() + theLeftZoneAnnotationWidth + theDecoratedNounBitmapZoom.getWidth() + (aGlyphZoomSize.getDecoratedNounMargin() * 2) + 6;
		for (String theAnnotation : theRightZoneAnnotationList) {
			theCanvas.drawText(
					theAnnotation,
					theLeftMargin,
					aGlyphZoomSize.getAnnotationTopMarginHeight() + aGlyphZoomSize.getAnnotationTopZoneHeight() + (aGlyphZoomSize.getDecoratedNounMargin() * 2) + (theRow * aGlyphZoomSize.getAnnotationCharacterHeight()) + 35,
					thePaint );
			++theRow;
		}
		return theAnnotatedBitmap;
	}
	
	public ArrayList<String> getCanvasZoneAnnotations(CanvasZone aCanvasZone) {
		ArrayList<String> theStringArrayList = new ArrayList<String>();
		if (DecKanGlDictionary.decoratorsUseZone(this.decoratorMap, aCanvasZone)) {
			switch(aCanvasZone) {
				case BOTTOM_ZONE:
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.bottom_zone_LEFT)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.bottom_zone_MIDDLE)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.bottom_zone_RIGHT)));
					break;
				case LEFT_ZONE:
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.left_zone_TOP)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.left_zone_MIDDLE)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.left_zone_BOTTOM)));
					break;
				case RIGHT_ZONE:
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.right_zone_TOP)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.right_zone_MIDDLE)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.right_zone_BOTTOM)));
					break;
				case TOP_ZONE:
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.top_zone_LEFT)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.top_zone_MIDDLE)));
					theStringArrayList.add(getAnnotation(getDecoratorForLocation(DecoratorCanvasLocation.top_zone_RIGHT)));
					break;
				default:
					// TODO - throw a nasty exception about bad programmers!  ;-)
			}
		}
		return theStringArrayList;
	}

	
	private static int getMaximumAnnotationLength(ArrayList<String> anAnnotationList) {
		int theMaximumLength = 0;
		for (String theAnnotation : anAnnotationList) {
			theMaximumLength = theAnnotation.length() > theMaximumLength ? theAnnotation.length() : theMaximumLength;
		}
		return theMaximumLength;
	}

	public HashMap<DecoratorCanvasLocation, DecKanGlDecorator> getDecoratorMap() {
		return this.decoratorMap;
	}

	public DecKanGlDecorator getDecorator(String aDecoratorName) {
		return this.decoratorMap.get(aDecoratorName);
	}

	public NounDefinition getNounDefinition() {
		return getNounWorkStatus().getNoun();
	}

	private static String getAnnotation(DecKanGlDecorator aDecorator) {
		if (aDecorator == null) {
			return "";
		}
		return aDecorator.getAnnotation();
	}

	private DecKanGlDecorator getDecoratorForLocation(
			DecoratorCanvasLocation aCanvasLocation) {
		for (DecKanGlDecorator theDecKanGlDecorator : this.decoratorMap.values()) {
			if (theDecKanGlDecorator.getTribKnQualityMetric().getLogicalCanvasLocation() == aCanvasLocation) {
				return theDecKanGlDecorator;
			}
		}
		return null;
	}

	public BitmapDrawable getDecoratedNounBitmapDrawable() {
		return new BitmapDrawable(FmsLibraryApplication.getContext().getResources(),getDecoratedNounBitmap());
	}

	public BitmapDrawable getAnnotatedBitmapDrawable(GlyphZoomSize aGlyphZoomSize) {
		return new BitmapDrawable(FmsLibraryApplication.getContext().getResources(),getAnnotatedBitmap(aGlyphZoomSize));
	}

	public BitmapDrawable getDecoratedNounBitmapDrawableZoom(GlyphZoomSize aGlyphZoomSize) {
		return new BitmapDrawable(FmsLibraryApplication.getContext().getResources(), getDecoratedNounBitmapZoom(aGlyphZoomSize));
	}
	
	public int getNodeQualityIndex() {
		int theNodeQualityIndex = 0;
		for (DecKanGlDecorator theDecKanGlDecorator : getDecoratorMap().values()) {
			theNodeQualityIndex += theDecKanGlDecorator.getTribKnQualityIndex();
		}
		return theNodeQualityIndex;
	}
	
	public String getNodeQualityIndexString() {
		return "<< " + getNodeQualityIndex() + " >>";
	}

}